#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
散点图绘制工具
版本: 1.2.0
作者: 陈振玺
功能: 支持多种数据格式的散点图绘制，包含颜色映射、形状映射、拟合曲线、参考线和seaborn样式等功能
"""

# 所有包导入必须放在文件头部
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import argparse
import sys
from pathlib import Path
from scipy import stats
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.metrics import r2_score
import warnings
import os
from typing import Optional, List, Tuple, Dict, Any

# 设置matplotlib中文字体支持
plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Arial', 'sans-serif']
plt.rcParams['axes.unicode_minus'] = False


class ScatterPlotter:
    """
    散点图绘制工具类
    
    功能特性:
    - 支持多种数据格式读取 (CSV, TSV, XLSX)
    - 支持颜色映射和形状映射
    - 支持拟合曲线绘制
    - 支持X轴和Y轴参考线绘制
    - 支持seaborn样式和自动图例优化
    - 支持图表导出
    """
    
    def __init__(self, use_seaborn: bool = True, seaborn_style: str = 'whitegrid',
                 seaborn_palette: str = 'deep'):
        """
        初始化散点图绘制工具
        
        Args:
            use_seaborn: 是否使用seaborn样式（默认True）
            seaborn_style: seaborn样式主题（默认'whitegrid'）
            seaborn_palette: seaborn调色板（默认'deep'）
        """
        self.data = None
        self.x_column = None
        self.y_column = None
        self.color_column = None
        self.shape_column = None
        self.fit_line = False
        self.fit_method = 'linear'  # 'linear' 或 'polynomial'
        self.poly_degree = 2
        
        # Seaborn配置
        self.use_seaborn = use_seaborn
        self.seaborn_style = seaborn_style
        self.seaborn_palette = seaborn_palette
        
        # 设置seaborn样式
        if self.use_seaborn:
            sns.set_style(self.seaborn_style)
            sns.set_palette(self.seaborn_palette)
        
        # 参考线配置
        self.x_reference_lines = []  # X轴参考线位置列表
        self.y_reference_lines = []  # Y轴参考线位置列表
        self.reference_line_style = '--'  # 参考线样式
        self.reference_line_color = 'red'  # 参考线颜色
        self.reference_line_alpha = 0.7  # 参考线透明度
        self.reference_line_width = 1.0  # 参考线宽度
        
        # 预定义的颜色和形状映射（seaborn优先）
        if self.use_seaborn:
            self.colors = sns.color_palette(self.seaborn_palette, 10)
        else:
            self.colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', 
                          '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']
        self.markers = ['o', 's', '^', 'D', 'v', '<', '>', 'p', '*', 'h']
    
    def set_reference_lines(self, x_lines: Optional[List[float]] = None, 
                           y_lines: Optional[List[float]] = None,
                           style: str = '--', color: str = 'red', 
                           alpha: float = 0.7, width: float = 1.0):
        """
        设置参考线配置
        
        Args:
            x_lines: X轴参考线位置列表
            y_lines: Y轴参考线位置列表
            style: 参考线样式 (默认: '--')
            color: 参考线颜色 (默认: 'red')
            alpha: 参考线透明度 (默认: 0.7)
            width: 参考线宽度 (默认: 1.0)
        """
        if x_lines:
            self.x_reference_lines = x_lines
        if y_lines:
            self.y_reference_lines = y_lines
        
        self.reference_line_style = style
        self.reference_line_color = color
        self.reference_line_alpha = alpha
        self.reference_line_width = width
    
    def load_data(self, file_path: str) -> bool:
        """
        加载数据文件
        
        Args:
            file_path: 数据文件路径
            
        Returns:
            bool: 加载成功返回True，失败返回False
        """
        try:
            file_path = Path(file_path)
            
            if not file_path.exists():
                print(f"错误: 文件 {file_path} 不存在")
                return False
            
            # 根据文件扩展名选择读取方法
            if file_path.suffix.lower() == '.csv':
                self.data = pd.read_csv(file_path)
            elif file_path.suffix.lower() == '.tsv':
                self.data = pd.read_csv(file_path, sep='\t')
            elif file_path.suffix.lower() in ['.xlsx', '.xls']:
                self.data = pd.read_excel(file_path)
            else:
                print(f"错误: 不支持的文件格式 {file_path.suffix}")
                return False
            
            print(f"成功加载数据: {len(self.data)} 行, {len(self.data.columns)} 列")
            print(f"列名: {list(self.data.columns)}")
            return True
            
        except Exception as e:
            print(f"加载数据时发生错误: {str(e)}")
            return False
    
    def validate_columns(self, x_column: str, y_column: str, 
                        color_column: Optional[str] = None, 
                        shape_column: Optional[str] = None) -> bool:
        """
        验证列名是否存在且为数值类型
        
        Args:
            x_column: X轴数据列名
            y_column: Y轴数据列名
            color_column: 颜色映射列名（可选）
            shape_column: 形状映射列名（可选）
            
        Returns:
            bool: 验证通过返回True，否则返回False
        """
        if self.data is None:
            print("错误: 请先加载数据")
            return False
        
        # 检查必需列是否存在
        required_columns = [x_column, y_column]
        for col in required_columns:
            if col not in self.data.columns:
                print(f"错误: 列 '{col}' 不存在")
                return False
        
        # 检查X轴和Y轴列是否为数值类型
        for col in [x_column, y_column]:
            if not pd.api.types.is_numeric_dtype(self.data[col]):
                print(f"错误: 列 '{col}' 不是数值类型")
                return False
        
        # 检查可选列是否存在
        optional_columns = [color_column, shape_column]
        for col in optional_columns:
            if col is not None and col not in self.data.columns:
                print(f"错误: 列 '{col}' 不存在")
                return False
        
        # 设置列名
        self.x_column = x_column
        self.y_column = y_column
        self.color_column = color_column
        self.shape_column = shape_column
        
        return True
    
    def prepare_mapping_data(self) -> Tuple[Optional[np.ndarray], Optional[np.ndarray]]:
        """
        准备颜色和形状映射数据
        
        Returns:
            Tuple[Optional[np.ndarray], Optional[np.ndarray]]: 颜色映射数组和形状映射数组
        """
        color_map = None
        shape_map = None
        
        # 处理颜色映射
        if self.color_column:
            if pd.api.types.is_numeric_dtype(self.data[self.color_column]):
                # 数值类型直接使用
                color_map = self.data[self.color_column].values
            else:
                # 分类类型进行编码
                le = LabelEncoder()
                color_map = le.fit_transform(self.data[self.color_column].values)
        
        # 处理形状映射
        if self.shape_column:
            if pd.api.types.is_numeric_dtype(self.data[self.shape_column]):
                # 数值类型转换为分类
                unique_values = sorted(self.data[self.shape_column].unique())
                shape_map = self.data[self.shape_column].map(
                    {val: i for i, val in enumerate(unique_values)}
                ).values
            else:
                # 分类类型进行编码
                le = LabelEncoder()
                shape_map = le.fit_transform(self.data[self.shape_column].values)
        
        return color_map, shape_map
    
    def calculate_fit_line(self, x_data: np.ndarray, y_data: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        """
        计算拟合曲线
        
        Args:
            x_data: X轴数据
            y_data: Y轴数据
            
        Returns:
            Tuple[np.ndarray, np.ndarray]: 拟合曲线的X和Y坐标
        """
        # 移除NaN值
        mask = ~(np.isnan(x_data) | np.isnan(y_data))
        x_clean = x_data[mask]
        y_clean = y_data[mask]
        
        if len(x_clean) < 2:
            return np.array([]), np.array([])
        
        # 生成拟合曲线的X坐标
        x_fit = np.linspace(x_clean.min(), x_clean.max(), 100)
        
        try:
            if self.fit_method == 'linear':
                # 线性拟合
                slope, intercept, r_value, p_value, std_err = stats.linregress(x_clean, y_clean)
                y_fit = slope * x_fit + intercept
                print(f"线性拟合结果: R² = {r_value**2:.4f}, p-value = {p_value:.4f}")
            
            elif self.fit_method == 'polynomial':
                # 多项式拟合
                poly_features = PolynomialFeatures(degree=self.poly_degree)
                poly_reg = Pipeline([
                    ('poly', poly_features),
                    ('linear', LinearRegression())
                ])
                
                poly_reg.fit(x_clean.reshape(-1, 1), y_clean)
                y_fit = poly_reg.predict(x_fit.reshape(-1, 1))
                
                # 计算R²
                y_pred = poly_reg.predict(x_clean.reshape(-1, 1))
                r2 = poly_reg.score(x_clean.reshape(-1, 1), y_clean)
                print(f"多项式拟合结果 (degree={self.poly_degree}): R² = {r2:.4f}")
            
            else:
                return np.array([]), np.array([])
            
            return x_fit, y_fit
            
        except Exception as e:
            print(f"拟合曲线计算失败: {str(e)}")
            return np.array([]), np.array([])
    
    def create_scatter_plot(self, figsize: Tuple[int, int] = (10, 8), 
                           title: Optional[str] = None, 
                           alpha: float = 0.7, 
                           s: float = 50) -> plt.Figure:
        """
        创建散点图
        
        Args:
            figsize: 图形大小
            title: 图表标题
            alpha: 点的透明度
            s: 点的大小
            
        Returns:
            plt.Figure: matplotlib图形对象
        """
        if self.data is None or self.x_column is None or self.y_column is None:
            raise ValueError("请先加载数据并设置X、Y轴列名")
        
        # 获取数据
        x_data = self.data[self.x_column].values
        y_data = self.data[self.y_column].values
        
        # 准备映射数据
        color_map, shape_map = self.prepare_mapping_data()
        
        # 创建图形
        fig, ax = plt.subplots(figsize=figsize)
        
        # 绘制散点图
        if self.use_seaborn and (color_map is not None or shape_map is not None):
            # 使用seaborn绘制带有颜色和形状映射的散点图
            plot_data = pd.DataFrame({
                self.x_column: x_data,
                self.y_column: y_data
            })
            
            # 添加颜色和形状映射列
            hue_col = None
            style_col = None
            
            if color_map is not None:
                hue_col = self.color_column
                plot_data[hue_col] = self.data[self.color_column].values
            
            if shape_map is not None:
                style_col = self.shape_column
                plot_data[style_col] = self.data[self.shape_column].values
            
            # 使用seaborn绘制散点图
            scatter_plot = sns.scatterplot(
                data=plot_data,
                x=self.x_column,
                y=self.y_column,
                hue=hue_col,
                style=style_col,
                alpha=alpha,
                s=s,
                ax=ax
            )
            
            # 调整图例位置
            if hue_col or style_col:
                handles, labels = ax.get_legend_handles_labels()
                if handles:
                    ax.legend(handles=handles, labels=labels, 
                             loc='center left', bbox_to_anchor=(1, 0.5),
                             frameon=True, fancybox=True, shadow=True, fontsize=10)
        
        else:
            # 使用matplotlib绘制基础散点图或回退模式
            legend_elements = []  # 用于存储图例元素
            
            if color_map is not None and shape_map is not None:
                # 同时使用颜色和形状映射
                unique_shapes = np.unique(shape_map)
                unique_colors = np.unique(color_map)
                
                # 创建颜色映射字典
                if pd.api.types.is_numeric_dtype(self.data[self.color_column]):
                    # 数值型颜色映射，使用颜色条
                    for shape_idx in unique_shapes:
                        mask = shape_map == shape_idx
                        marker = self.markers[shape_idx % len(self.markers)]
                        
                        scatter = ax.scatter(x_data[mask], y_data[mask], 
                                           c=color_map[mask], 
                                           marker=marker, 
                                           alpha=alpha, s=s, 
                                           cmap='viridis')
                        
                        # 添加形状图例
                        shape_value = self.data[self.shape_column].iloc[np.where(shape_map == shape_idx)[0][0]]
                        legend_elements.append(plt.Line2D([0], [0], marker=marker, color='gray', 
                                                        linestyle='None', markersize=8, 
                                                        label=f'{self.shape_column}: {shape_value}'))
                    
                    # 添加颜色条
                    cbar = plt.colorbar(scatter, ax=ax)
                    cbar.set_label(self.color_column, fontsize=12)
                else:
                    # 分类型颜色映射
                    for shape_idx in unique_shapes:
                        for color_idx in unique_colors:
                            mask = (shape_map == shape_idx) & (color_map == color_idx)
                            if np.any(mask):
                                marker = self.markers[shape_idx % len(self.markers)]
                                color = self.colors[color_idx % len(self.colors)]
                                
                                ax.scatter(x_data[mask], y_data[mask], 
                                         marker=marker, color=color, 
                                         alpha=alpha, s=s)
                                
                                # 获取原始标签值
                                shape_value = self.data[self.shape_column].iloc[np.where(shape_map == shape_idx)[0][0]]
                                color_value = self.data[self.color_column].iloc[np.where(color_map == color_idx)[0][0]]
                                
                                legend_elements.append(plt.Line2D([0], [0], marker=marker, color=color, 
                                                                linestyle='None', markersize=8, 
                                                                label=f'{self.shape_column}: {shape_value}, {self.color_column}: {color_value}'))
            
            elif color_map is not None:
                # 仅使用颜色映射
                if pd.api.types.is_numeric_dtype(self.data[self.color_column]):
                    # 数值型颜色映射，使用颜色条
                    scatter = ax.scatter(x_data, y_data, c=color_map, 
                                       alpha=alpha, s=s, cmap='viridis')
                    cbar = plt.colorbar(scatter, ax=ax)
                    cbar.set_label(self.color_column, fontsize=12)
                else:
                    # 分类型颜色映射，使用图例
                    unique_colors = np.unique(color_map)
                    for color_idx in unique_colors:
                        mask = color_map == color_idx
                        color = self.colors[color_idx % len(self.colors)]
                        
                        ax.scatter(x_data[mask], y_data[mask], 
                                 color=color, alpha=alpha, s=s)
                        
                        # 获取原始标签值
                        color_value = self.data[self.color_column].iloc[np.where(color_map == color_idx)[0][0]]
                        legend_elements.append(plt.Line2D([0], [0], marker='o', color=color, 
                                                        linestyle='None', markersize=8, 
                                                        label=f'{self.color_column}: {color_value}'))
            
            elif shape_map is not None:
                # 仅使用形状映射
                unique_shapes = np.unique(shape_map)
                for shape_idx in unique_shapes:
                    mask = shape_map == shape_idx
                    marker = self.markers[shape_idx % len(self.markers)]
                    color = self.colors[shape_idx % len(self.colors)]
                    
                    ax.scatter(x_data[mask], y_data[mask], 
                              marker=marker, color=color, 
                              alpha=alpha, s=s)
                    
                    # 获取原始标签值
                    shape_value = self.data[self.shape_column].iloc[np.where(shape_map == shape_idx)[0][0]]
                    legend_elements.append(plt.Line2D([0], [0], marker=marker, color=color, 
                                                    linestyle='None', markersize=8, 
                                                    label=f'{self.shape_column}: {shape_value}'))
            
            else:
                # 基础散点图
                if self.use_seaborn:
                    sns.scatterplot(x=x_data, y=y_data, alpha=alpha, s=s, ax=ax)
                else:
                    ax.scatter(x_data, y_data, alpha=alpha, s=s, color='#1f77b4')
            
            # 添加图例（matplotlib模式）
            if legend_elements:
                ax.legend(handles=legend_elements, loc='center left', bbox_to_anchor=(1, 0.5), 
                         frameon=True, fancybox=True, shadow=True, fontsize=10)
        

        
        # 添加拟合曲线
        if self.fit_line:
            x_fit, y_fit = self.calculate_fit_line(x_data, y_data)
            if len(x_fit) > 0:
                ax.plot(x_fit, y_fit, 'r-', linewidth=2, 
                       label=f'{self.fit_method.title()} Fit')
                ax.legend()
        
        # 设置标签和标题
        ax.set_xlabel(self.x_column, fontsize=12)
        ax.set_ylabel(self.y_column, fontsize=12)
        
        if title:
            ax.set_title(title, fontsize=14, fontweight='bold')
        else:
            ax.set_title(f'Scatter Plot: {self.y_column} vs {self.x_column}', 
                        fontsize=14, fontweight='bold')
        
        # 添加网格
        ax.grid(True, alpha=0.3)
        
        # 添加参考线
        self._add_reference_lines(ax)
        
        # 调整布局
        plt.tight_layout()
        
        return fig
    
    def _add_reference_lines(self, ax):
        """
        添加参考线到图表
        
        Args:
            ax: matplotlib轴对象
        """
        # 添加X轴参考线（垂直线）
        for x_pos in self.x_reference_lines:
            ax.axvline(x=x_pos, 
                      linestyle=self.reference_line_style,
                      color=self.reference_line_color,
                      alpha=self.reference_line_alpha,
                      linewidth=self.reference_line_width,
                      label=f'X参考线: {x_pos}' if len(self.x_reference_lines) == 1 else None)
        
        # 添加Y轴参考线（水平线）
        for y_pos in self.y_reference_lines:
            ax.axhline(y=y_pos, 
                      linestyle=self.reference_line_style,
                      color=self.reference_line_color,
                      alpha=self.reference_line_alpha,
                      linewidth=self.reference_line_width,
                      label=f'Y参考线: {y_pos}' if len(self.y_reference_lines) == 1 else None)
        
        # 如果有参考线且只有一条，添加到图例中
        if (len(self.x_reference_lines) == 1 and len(self.y_reference_lines) == 0) or \
           (len(self.y_reference_lines) == 1 and len(self.x_reference_lines) == 0):
            ax.legend()
    
    def export_plot(self, output_path: str, dpi: int = 300, 
                   format: str = 'png') -> bool:
        """
        导出图表
        
        Args:
            output_path: 输出文件路径
            dpi: 图像分辨率
            format: 输出格式 ('png', 'pdf', 'svg', 'jpg')
            
        Returns:
            bool: 导出成功返回True，失败返回False
        """
        try:
            fig = self.create_scatter_plot()
            
            # 确保输出目录存在
            output_path = Path(output_path)
            output_path.parent.mkdir(parents=True, exist_ok=True)
            
            # 保存图表
            fig.savefig(output_path, dpi=dpi, format=format, 
                       bbox_inches='tight', facecolor='white')
            
            plt.close(fig)
            print(f"图表已保存到: {output_path}")
            return True
            
        except Exception as e:
            print(f"导出图表时发生错误: {str(e)}")
            return False
    
    def print_data_summary(self):
        """
        打印数据摘要信息
        """
        if self.data is None:
            print("未加载数据")
            return
        
        print("\n=== 数据摘要 ===")
        print(f"数据形状: {self.data.shape}")
        print(f"列名: {list(self.data.columns)}")
        
        if self.x_column and self.y_column:
            print(f"\nX轴列 ({self.x_column}) 统计:")
            print(self.data[self.x_column].describe())
            
            print(f"\nY轴列 ({self.y_column}) 统计:")
            print(self.data[self.y_column].describe())
            
            if self.color_column:
                print(f"\n颜色映射列 ({self.color_column}) 信息:")
                if pd.api.types.is_numeric_dtype(self.data[self.color_column]):
                    print(self.data[self.color_column].describe())
                else:
                    print(self.data[self.color_column].value_counts())
            
            if self.shape_column:
                print(f"\n形状映射列 ({self.shape_column}) 信息:")
                if pd.api.types.is_numeric_dtype(self.data[self.shape_column]):
                    print(self.data[self.shape_column].describe())
                else:
                    print(self.data[self.shape_column].value_counts())


def main():
    """
    主函数 - 命令行接口
    """
    parser = argparse.ArgumentParser(
        description='散点图绘制工具 - 支持多种数据格式和可视化配置',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  # 基础散点图（默认使用seaborn样式）
  python scatter_plotter.py -i data.csv -x column1 -y column2 -o output.png
  
  # 使用matplotlib原生样式
  python scatter_plotter.py -i data.xlsx -x x_col -y y_col --no-seaborn -o output.pdf
  
  # 自定义seaborn样式和调色板
  python scatter_plotter.py -i data.csv -x x_col -y y_col --seaborn-style dark --seaborn-palette bright -o output.png
  
  # 带颜色映射的散点图（seaborn自动处理颜色和图例）
  python scatter_plotter.py -i data.xlsx -x x_col -y y_col -c color_col -o output.pdf
  
  # 带形状映射和拟合曲线的散点图（seaborn自动优化图例显示）
  python scatter_plotter.py -i data.tsv -x x_col -y y_col -s shape_col --fit linear -o output.svg
  
  # 带参考线的散点图
  python scatter_plotter.py -i data.csv -x x_col -y y_col --x-ref 0 5 --y-ref 2.5 -o output.png
  
  # 自定义参考线样式的散点图
  python scatter_plotter.py -i data.csv -x x_col -y y_col --x-ref 0 --ref-color blue --ref-style ':' -o output.png
        """
    )
    
    # 必需参数
    parser.add_argument('-i', '--input', required=True,
                       help='输入数据文件路径 (支持CSV/TSV/XLSX格式)')
    parser.add_argument('-x', '--x-column', required=True,
                       help='X轴数据列名')
    parser.add_argument('-y', '--y-column', required=True,
                       help='Y轴数据列名')
    parser.add_argument('-o', '--output', required=True,
                       help='输出图表文件路径')
    
    # 可选参数
    parser.add_argument('-c', '--color-column',
                       help='颜色映射列名')
    parser.add_argument('-s', '--shape-column',
                       help='形状映射列名')
    parser.add_argument('--fit', choices=['linear', 'polynomial'],
                       help='添加拟合曲线类型')
    parser.add_argument('--poly-degree', type=int, default=2,
                       help='多项式拟合的阶数 (默认: 2)')
    parser.add_argument('--title',
                       help='图表标题')
    parser.add_argument('--figsize', nargs=2, type=int, default=[10, 8],
                       help='图形大小 (宽度 高度) (默认: 10 8)')
    parser.add_argument('--alpha', type=float, default=0.7,
                       help='点的透明度 (0-1) (默认: 0.7)')
    parser.add_argument('--size', type=float, default=50,
                       help='点的大小 (默认: 50)')
    parser.add_argument('--dpi', type=int, default=300,
                       help='输出图像分辨率 (默认: 300)')
    parser.add_argument('--format', choices=['png', 'pdf', 'svg', 'jpg'], 
                       default='png',
                       help='输出图像格式 (默认: png)')
    parser.add_argument('--summary', action='store_true',
                       help='显示数据摘要信息')
    
    # 参考线参数
    parser.add_argument('--x-ref', nargs='*', type=float,
                       help='X轴参考线位置 (可指定多个值，如: --x-ref 0 5.5 10)')
    parser.add_argument('--y-ref', nargs='*', type=float,
                       help='Y轴参考线位置 (可指定多个值，如: --y-ref 0 2.5 5)')
    parser.add_argument('--ref-style', default='--',
                       help='参考线样式 (默认: --)')
    parser.add_argument('--ref-color', default='red',
                       help='参考线颜色 (默认: red)')
    parser.add_argument('--ref-alpha', type=float, default=0.7,
                       help='参考线透明度 (0-1) (默认: 0.7)')
    parser.add_argument('--ref-width', type=float, default=1.0,
                       help='参考线宽度 (默认: 1.0)')
    
    # Seaborn样式参数
    parser.add_argument('--use-seaborn', action='store_true', default=True,
                       help='使用seaborn样式（默认启用）')
    parser.add_argument('--no-seaborn', action='store_true', 
                       help='禁用seaborn样式，使用matplotlib原生样式')
    parser.add_argument('--seaborn-style', type=str, default='whitegrid',
                       choices=['darkgrid', 'whitegrid', 'dark', 'white', 'ticks'],
                       help='seaborn样式主题（默认: whitegrid）')
    parser.add_argument('--seaborn-palette', type=str, default='deep',
                       choices=['deep', 'muted', 'bright', 'pastel', 'dark', 'colorblind'],
                       help='seaborn调色板（默认: deep）')
    
    args = parser.parse_args()
    
    try:
        # 处理seaborn参数
        use_seaborn = args.use_seaborn and not args.no_seaborn
        
        # 创建散点图绘制工具实例
        plotter = ScatterPlotter(
            use_seaborn=use_seaborn,
            seaborn_style=args.seaborn_style,
            seaborn_palette=args.seaborn_palette
        )
        
        # 加载数据
        if not plotter.load_data(args.input):
            return 1
        
        # 验证列名
        if not plotter.validate_columns(args.x_column, args.y_column, 
                                       args.color_column, args.shape_column):
            return 1
        
        # 设置拟合参数
        if args.fit:
            plotter.fit_line = True
            plotter.fit_method = args.fit
            plotter.poly_degree = args.poly_degree
        
        # 设置参考线
        if args.x_ref or args.y_ref:
            plotter.set_reference_lines(
                x_lines=args.x_ref if args.x_ref else None,
                y_lines=args.y_ref if args.y_ref else None,
                style=args.ref_style,
                color=args.ref_color,
                alpha=args.ref_alpha,
                width=args.ref_width
            )
        
        # 显示数据摘要
        if args.summary:
            plotter.print_data_summary()
        
        # 创建并导出图表
        print("\n正在生成散点图...")
        
        # 确定输出格式
        output_format = args.format
        if not output_format:
            output_format = Path(args.output).suffix[1:] or 'png'
        
        # 导出图表
        success = plotter.export_plot(
            args.output, 
            dpi=args.dpi, 
            format=output_format
        )
        
        if success:
            print("散点图生成完成!")
            return 0
        else:
            print("散点图生成失败!")
            return 1
            
    except KeyboardInterrupt:
        print("\n用户中断操作")
        return 1
    except Exception as e:
        print(f"程序执行出错: {str(e)}")
        return 1


if __name__ == '__main__':
    exit(main())